# -*- coding: utf-8 -*-
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# Author：'lilee'
# Date：2018/6/25
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

from odoo import models, fields, api
from odoo.exceptions import UserError
from datetime import datetime

class Expense(models.Model):
    _name = 'evection.expense'
    _inherit = ['mail.thread', 'common.state.methods']
    _order = 'id desc'

    @api.model
    def _get_employee(self):
        employee = self.env['hr.employee'].search([('user_id', '=', self._uid)])
        if employee:
            return employee.ids[0]
        else:
            return None

    write_uid = fields.Many2one('res.users', string='最后更新者', index=True, readonly=True, track_visibility='onchange')
    write_date = fields.Datetime(string='最近修改时间', readonly=True, track_visibility='onchange')
    name = fields.Char(string="单号")
    title = fields.Char(string="标题")
    date = fields.Date('费用报销日期', default=lambda x :datetime.now())
    occur_date = fields.Date(u'费用发生日期')
    pick_date = fields.Date('费用领用日期')
    employee_id = fields.Many2one('hr.employee', string="申请人", default=_get_employee)
    type = fields.Selection([('internal', u'新得利费用报销单'), ('travel', u'新得利出差费用报销单')]
                            , string=u'费用类型')
    relation_item = fields.Selection([('invoice', u'报销原始凭证为发票'), ('not_invoice', u'报销原始凭证为非发票')],
                                     string=u'关联项目', default="invoice")
    amount_total = fields.Float('总计', compute="_compute_amount_total", store=True)
    expense_lines = fields.One2many('expense.line', 'evection_expense_id', string='费用明细')
    expense_expense_id = fields.Many2one('expense.expense', string="出差申请单")
    expense_num = fields.Char('费用号')
    description = fields.Char('备注')
    state = fields.Selection([
        ('draft', u'待提交'),
        ('department_approval', u'部门经理审批'),
        ('financial_approval', u'财务审批'),
        ('manager_approval', u'总经办审批'),
        ('finance_done', '财务确认'),
        ('done', u'已完成'),
        ('cancel', '取消'),
    ], string='Status', copy=False, index=True, readonly=True, store=True, default="draft")
    request_state = fields.Selection([
        ('paid', '已支付'),
        ('partly', '部分支付'),
        ('not_paid', '未支付'),
    ], string='支付状态', copy=False, index=True, store=True, compute="_compute_request_state")
    tickets_quantity = fields.Integer('单据张数', compute="_compute_tickets_quantity", store=True)
    responsible_employee = fields.Many2many('hr.employee', string="明细承担人", store=True,
                                            compute="_compute_responsible_employee")

    @api.depends('expense_lines', 'expense_lines.request_state')
    def _compute_request_state(self):
        for order in self:
            states = set(order.expense_lines.mapped('request_state'))
            if len(states) == 1 and 'requested' in states:
                order.request_state = 'paid'
            elif len(states) == 1 and 'requested' not in states:
                order.request_state = 'not_paid'
            elif len(states) > 1 and 'requested' in states:
                order.request_state = 'partly'
            else:
                order.request_state = 'not_paid'

    @api.depends('expense_lines', 'expense_lines.employee_id_2')
    def _compute_responsible_employee(self):
        for order in self:
            ids = []
            for line in order.expense_lines:
                ids.append(line.employee_id_2.id)
            order.responsible_employee = [(6, 0, ids)]


    @api.depends('expense_lines.ticket_quantity')
    def _compute_tickets_quantity(self):
        for expense in self:
            if expense.expense_lines:
                try:
                    total_list = [int(x) for x in expense.expense_lines.mapped('ticket_quantity')]
                    expense.tickets_quantity = sum(total_list)
                except ValueError:
                    raise UserError('费用明细中的“单据张数”项有非法字符')

    @api.multi
    def action_back(self):
        # 退回按钮，使状态退回上一级 TODO 如何只显示当前的审核人？
        self.ensure_one()
        if self.state == 'department_approval':
            self.state = 'draft'
        elif self.state == 'financial_approval':
            self.state = 'department_approval'
        elif self.state == 'manager_approval':
            self.state = 'financial_approval'
        elif self.state == 'finance_done':
            self.state = 'manager_approval'
        elif self.state == 'done':
            self.state = 'finance_done'
        else:
            return

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state != 'draft':
                raise UserError('不能删除处于非草稿状态的费用报销单据！')
        return super(Expense, self).unlink()


    @api.depends('expense_lines.price')
    def _compute_amount_total(self):
        for expense in self:
            expense.amount_total = sum(expense.expense_lines.mapped('price'))

    @api.multi
    def action_draft(self):
        # 限制“出差补助”费用产品，只能在出差申请里才能填写，也就是在没有order_id的情况下，
        # 产品不能为“出差补助”
        for order in self:
            for line in order.expense_lines:
                if line.name.can_be_expensed != True:
                    raise UserError('{0.name}不是费用产品')
                if (line.name.name in ['出差补助', '出差交通费'] or line.name.product_size in ['出差补助', '出差交通费']) and not line.order_id:
                    raise UserError('出差补助只能在出差申请里报销！')
            if order.state == 'draft':
                order.write({"state": "department_approval"})


    @api.model
    def create(self, vals):
        res = super(Expense, self).create(vals)
        res.name = self.env['ir.sequence'].next_by_code('evection_expense.sequence')
        return res

    @api.multi
    def action_department_approval(self):
        if self.state == 'department_approval':
            self.write({"state": "financial_approval"})

    @api.multi
    def action_financial_approval(self):
        if self.state == 'financial_approval':
            self.write({"state": "manager_approval"})

    @api.multi
    def action_manager_approval(self):
        if self.state == 'manager_approval':
            self.write({"state": "done"})
            # 创建日记账分录
            self._create_account_move()


    def action_finance_done(self):
        # 此方法已失效
        if self.state == 'finance_done':
            self.write({"state": "done"})
            # 创建日记账分录
            self._create_account_move()


    @api.multi
    def cancel(self):
        account_move = self.env['account.move'].search([('origin_evection_expense', '=', self.id)])
        if account_move:
            raise UserError('已经生成日记账的费用不能取消')
        self.state = 'cancel'

    def _create_account_move(self):
        journal_id = self.env['account.journal'].search([('code', '=', 'FYBX')])
        if not journal_id:
            raise UserError('请先设置费用报销日记账，并设置其简码为FYBX')

        credit_account = self.employee_id.account_id
        if not credit_account:
            raise UserError('请为员工“%s”设置借还款科目！'%self.employee_id.name)
        credit_total_amount = sum(self.expense_lines.mapped('price'))

        line_list = []
        for line in self.expense_lines:
            debit_account = line.name.property_account_expense_id
            if not debit_account:
                raise UserError('请为费用产品“%s”配置费用科目！'%line.name.name)
            line_list.append((0, 0, {
                'name': line.name.name,
                'employee_id': line.employee_id_2.id,
                'debit': line.price,
                'account_id': debit_account.id,
                'ref': self.name
            }))
        account_move_info = {
            'date': datetime.now().date(),
            'journal_id': journal_id.id,
            'origin_evection_expense': self.id,
            'ref': self.name,
            'line_ids': line_list + [(0, 0, {
                'credit': credit_total_amount,
                'account_id': credit_account.id,
                'ref': self.name
            })]
        }
        account_move_id = self.env['account.move'].create(account_move_info)
        return account_move_id

    @api.multi
    def action_view_account_move(self):
        form_view_id = self.env.ref('account.view_move_form').id
        action = {
            'type': 'ir.actions.act_window',
            # 'views': [(form_view_id, 'form')],
            'view_mode': 'tree,form',
            'name': '日记账分录',
            'res_model': 'account.move',
            'domain':[('origin_evection_expense', '=', self.id)]
            # 'res_model':'consult.fee.request.line'
            }
        return action

class AccountMoveExtension(models.Model):
    _inherit = 'account.move'

    # 与费用报销联系
    origin_evection_expense = fields.Many2one('evection.expense')


class EvectionExpenseRequest(models.Model):
    _name = 'evection.expense.request'
    _inherit = ['mail.thread', 'common.state.methods']
    _rec_name = 'name'
    _order = 'id desc'

    write_uid = fields.Many2one('res.users', string='最后更新者', index=True, readonly=True, track_visibility='onchange')
    write_date = fields.Datetime(string='最近修改时间', readonly=True, track_visibility='onchange')
    name = fields.Char('支付单号')
    amount = fields.Float('支付总额', compute="_compute_amount", store=True)
    employee_id = fields.Many2one('hr.employee', string="申请人",
                                  compute="_compute_employee_id", store=True)
    department_id = fields.Many2one('hr.department', compute="_compute_employee_id", store=True)
    date = fields.Date('支付日期')
    journal_id = fields.Many2one('account.journal', string="付款方式",
                                 domain=[('type', 'in', ('cash', 'bank'))])

    line_ids = fields.One2many('expense.line', 'request_order_id', string="申请明细")
    state = fields.Selection([
        ('cash', '出纳审核'),
        ('draft', u'待支付'),
        ('confirm', '已支付'),
        ('cancel', '取消'),
        ], string='状态', copy=False, index=True,  default="cash", track_visibility="onchange")

    payment_date = fields.Datetime('借款日期', default=datetime.now())
    discription = fields.Char('备注')
    user_account_name = fields.Char('用户名',compute="_compute_employee_id", store=True)
    user_bank_name = fields.Char('开户行', compute="_compute_employee_id", store=True)
    user_bank_account = fields.Char('卡号', compute="_compute_employee_id", store=True)
    payment_employee_id = fields.Many2one('hr.employee', string="付款人")
    cash = fields.Boolean('需要出纳审核', compute="_compute_cash", store=True)

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state != 'cancel':
                raise UserError('不能删除处于非取消状态的付款审核单据！')
        return super(EvectionExpenseRequest, self).unlink()

    def _get_current_state_sequence(self):
        # 获取单据中的状态序列
        state_fields = self._fields.get('state')
        class_name = self.__class__.__name__
        if state_fields:
            states = list(zip(*state_fields.selection))[0]
            if self.cash:
                states = states[states.index('cash'):]
            else:
                states = states[states.index('draft'):]
            return states
        else:
            raise UserError('表单“%s”中没有找到“state”字段！' % class_name)

    @api.depends('journal_id')
    def _compute_cash(self):
        for order in self:
            if order.journal_id.type == 'cash' or \
                    (order.journal_id.type == 'bank' and not order.journal_id.to_private):
                order.cash = True
            else:
                order.cash = False

    @api.onchange('employee_id')
    def _onchange_bank_account(self):
        for request in self:
            employee = request.employee_id
            request.user_account_name = employee.name
            request.user_bank_name = employee.private_open_bank
            request.user_bank_account = employee.private_bank_account


    @api.depends('line_ids.request_employee_id')
    def _compute_employee_id(self):
        for request in self:
            if request.line_ids:
                employee_id = request.line_ids[0].request_employee_id
                request.employee_id = employee_id.id
                request.department_id = employee_id.department_id.id

                request.user_account_name = employee_id.name
                request.user_bank_name = employee_id.private_open_bank
                request.user_bank_account = employee_id.private_bank_account

    @api.depends('line_ids.price')
    def _compute_amount(self):
        for request in self:
            total = sum(request.line_ids.mapped('price'))
            request.amount = total

    @api.multi
    def cash_confirm(self):
        if self.state != 'cash':
            return
        if not self.name:
            self.name = self.env['ir.sequence'].next_by_code('evection.expense.request')
        self.state = 'draft'

    @api.multi
    def action_confirm(self):
        if self.state == 'draft':
            if not self.name:
                self.name = self.env['ir.sequence'].next_by_code('evection.expense.request')
            self.state = 'confirm'
            # 更新付款人
            self.payment_employee_id = self.env.user.employee_ids[0]
            # 如果有的话，更新借款单的状态
            self._update_expense_from_borrow()
            # 更新明细到已支付状态
            # self._update_line_state()

    def _update_line_state(self):
        # 弃用此方法
        for order in self:
            order.line_ids.write({'state': 'requested'})

    def _update_expense_from_borrow(self):
        # 如果明细来自借款单，更新借款单的状态为“done”
        for order in self:
            for line in order.line_ids:
                if line.borrow_fee_id:
                    line.borrow_fee_id.write({'state':'done'})

    @api.multi
    def cancel(self):
        if self.state != 'cancel':
            self.state = 'cancel'

class EvectionExpenseTransient(models.TransientModel):

    _name = 'evection.expense.guide'

    journal_id = fields.Many2one('account.journal', string="付款方式",
                                 domain=[('type', 'in', ('cash', 'bank'))], required=True)

    @api.multi
    def confirm(self):
        # 勾选费用明细表，生成费用申请单
        ids = self.env.context.get('active_ids', [])
        for i in self.env['expense.line'].browse(ids):
            if i.request_state in ['will_requested', 'requested']:
                raise UserError('勾选的费用明细单据中有的已经申请了费用！')
            if not i.evection_expense_id and not i.borrow_fee_id:
                raise UserError('勾选的费用明细单据中有的没有所属费用单，也不是来自于借款单！')
            if i.state != 'done':
                raise UserError('勾选的单据 %s 没有审核完毕！' % i.show_name)

        if len(self.env['expense.line'].browse(ids).mapped('request_employee_id')) > 1:
            raise UserError('勾选的费用明细单据申请人不一致！')

        # 如果是现金或者公账，则需要额外一级审批
        if self.journal_id.type == 'cash' or (self.journal_id.type == 'bank' and not self.journal_id.to_private):
            state = 'cash'
        else:
            state = 'draft'

        request = self.env['evection.expense.request'].create({
            'date':datetime.now(),
            'journal_id':self.journal_id.id,
            'line_ids':[(6, 0, ids)],
            'state': state,
            })
        for line in self.env['expense.line'].browse(ids):
            line.write({'request_order_id':request.id, 'who_push_it':self.env.user.employee_ids[0].id})

        # action = self.env.ref('expense.expense_request_action').read()[0]
        # action['views'] = [(self.env.ref('expense.evection_expense_request_form_view').id, 'form') ]
        # action['res_id'] = request.id
        return


class ExpenseChartLine(models.Model):
    # 作废不用了
    _name = 'expense.chart.line'



    start_date = fields.Date('起始时间')
    end_time = fields.Date('结束时间')
    start_location = fields.Char('起始地点')
    end_location = fields.Char('结束地点')
    product_id = fields.Many2one('product.product', '交通')
    ticket_quantity = fields.Integer('单据张数')
    amount = fields.Float('金额')
    employee_id_2 = fields.Many2one('hr.employee', string="承担人")
    partner_id = fields.Many2one('res.partner', string="客户",
                                 domain=[('customer', '=', True)])
    # 出差补贴
    quantity = fields.Integer('人数')
    duration = fields.Integer('天数')
    standard = fields.Float('补贴标准')
    butie_fee = fields.Float('补贴金额', compute="_compute_butie_fee", store=True)
    description = fields.Text('备注')
    expense_id = fields.Many2one('expense.expense', '出差申请')
    is_applied = fields.Boolean('是否已申请', default=False)

    @api.depends('quantity', 'duration', 'standard')
    def _compute_butie_fee(self):
        for order in self:
            order.butie_fee = order.quantity * order.duration * order.standard

